Skip to content

fix: unwrap PSObject-wrapped values when serializing Invoke-MgGraphRequest dictionary bodies#3658

Open
Virgil-Bulens wants to merge 2 commits into
microsoftgraph:mainfrom
Virgil-Bulens:fix/3654-psobject-body-serialization
Open

fix: unwrap PSObject-wrapped values when serializing Invoke-MgGraphRequest dictionary bodies#3658
Virgil-Bulens wants to merge 2 commits into
microsoftgraph:mainfrom
Virgil-Bulens:fix/3654-psobject-body-serialization

Conversation

@Virgil-Bulens

Copy link
Copy Markdown

Fixes #3654

Changes proposed in this pull request

  • Add PSObjectJsonConverter, a Newtonsoft JsonConverter that unwraps PSObject-wrapped values to their BaseObject during serialization, at any nesting depth. Pure PSCustomObjects (which have no underlying CLR object) are projected into JSON objects from their properties.
  • Pass the converter at the single JsonConvert.SerializeObject call site in InvokeMgGraphRequest.SetRequestContent(HttpRequestMessage, IDictionary), so idiomatic hashtable bodies containing pipeline-produced values (bare $_ in ForEach-Object) serialize the underlying CLR values instead of failing with Self referencing loop detected for property 'Value' with type 'System.Management.Automation.PSParameterizedProperty'.
  • Add xUnit tests covering PSObject-wrapped strings/primitives/dictionaries, PSCustomObject bodies, and the SetRequestContent wiring (made internal; the assembly already has InternalsVisibleTo for the test project).
  • Pin the test project's Microsoft.PowerShell.SDK reference to 7.4.17. 7.5.x targets net9.0 only and contributes no assemblies to the net8.0 test build, which silently left the PowerShellStandard.Library stub (whose APIs return null) as the runtime System.Management.Automation. This is why the existing ShouldReturnPsObject tests fail on net8.0, and it made the new PSObject-dependent tests impossible to run. With the pin, the pre-existing StringUtilTests.ShouldReturnPsObject failures on net8.0 are also resolved.

Verification

  • dotnet test src/Authentication/Authentication.Test -f net8.0: 84/85 pass on Linux; the one failure (AuthenticationHelpersTests.ShouldThrowIfNonExistentCertNameIsProvidedAsync) is pre-existing on an unmodified checkout of main and unrelated (certificate store).
  • The exact repro from Invoke-MgGraphRequest -Body serialization fails on PSObject-wrapped pipeline strings #3654 was run in PowerShell 7.6 against the patched assembly and now produces:
    {"message":{"toRecipients":[{"emailAddress":{"address":"recipient@example.com"}}]},"saveToSentItems":true}
  • A nested [pscustomobject]@{ address = "..." } body value, which previously failed the same way, serializes to {"emailAddress":{"address":"recipient@example.com"}}.

Other links

…quest dictionary bodies

Invoke-MgGraphRequest -Body serializes IDictionary bodies with
Newtonsoft.Json. Values that came from the PowerShell pipeline (e.g.
bare $_ in ForEach-Object) are PSObject wrappers, so Newtonsoft
reflected over PowerShell adapted members (such as the Chars indexed
property on strings) and failed with a self-referencing loop error
before the request was sent.

Add a PSObjectJsonConverter that serializes the underlying BaseObject
of PSObject-wrapped values at any depth, and projects pure
PSCustomObjects into JSON objects, and pass it at the single
JsonConvert.SerializeObject call site for dictionary bodies.

Also pin the test project's Microsoft.PowerShell.SDK reference to the
latest 7.4.x release: 7.5.x targets net9.0 and contributes no
assemblies to the net8.0 test build, which silently left the
PowerShellStandard.Library stub (whose APIs return null) as the
runtime System.Management.Automation, making PSObject-dependent tests
impossible.

Fixes microsoftgraph#3654
@Virgil-Bulens

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@ramsessanchez ramsessanchez marked this pull request as ready for review July 14, 2026 18:52
@ramsessanchez ramsessanchez requested a review from a team as a code owner July 14, 2026 18:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Invoke-MgGraphRequest -Body JSON serialization failures caused by PowerShell pipeline values being wrapped as PSObject, by ensuring Newtonsoft.Json serializes the underlying CLR BaseObject instead of PowerShell-adapted members that can trigger self-referencing loops.

Changes:

  • Added PSObjectJsonConverter to unwrap PSObject values (and project pure PSCustomObject properties) during JSON serialization at any nesting depth.
  • Wired the converter into InvokeMgGraphRequest.SetRequestContent(HttpRequestMessage, IDictionary) and exposed the method as internal to enable direct testing.
  • Added xUnit tests for PSObject/PSCustomObject serialization behavior and pinned Microsoft.PowerShell.SDK to a net8-compatible 7.4.x version for the test target.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Authentication/Authentication/Helpers/PSObjectJsonConverter.cs Introduces a Json.NET converter that unwraps PSObject to prevent self-referencing loop serialization failures.
src/Authentication/Authentication/Cmdlets/InvokeMgGraphRequest.cs Uses the new converter when serializing IDictionary request bodies; makes the method internal for test access.
src/Authentication/Authentication.Test/Microsoft.Graph.Authentication.Test.csproj Pins Microsoft.PowerShell.SDK for net8.0 to ensure tests run against a real PowerShell runtime.
src/Authentication/Authentication.Test/Helpers/PSObjectJsonConverterTests.cs Adds coverage for nested PSObject-wrapped values, PSCustomObject projection, and the Invoke-MgGraphRequest wiring.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invoke-MgGraphRequest -Body serialization fails on PSObject-wrapped pipeline strings

3 participants